
`include "common_header.verilog"

//  *************************************************************************
//  File : rx_ff64v2ts
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2014 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler, Thomas Schrobenhauser
//  info@morethanip.com
//  *************************************************************************
//  Description : Receive FIFO Structure with timstamp and optional preamble
//                transfer at SOP.
//  Version     : $Id: rx_ff64v2ts.v,v 1.7 2017/01/03 00:11:01 dk Exp $
//  *************************************************************************

module rx_ff64v2ts (
   reset_rxclk,
   reset_ff_rx_clk,
   rx_err_frm_disc,
   rx_short_disc,
`ifdef MTIP_PARITY_PROT
   par_odd,
`endif
   rx_sw_reset,
   rx_sw_reset_done,
   rxclk,
   rx_stat_data,
   rx_stat_wren,
   rx_data_int,
   rx_a_full,
   rx_septy,
   rx_sop_int,
   rx_eop_int,
   rx_wren_int,
   rx_ts_int,
`ifdef USER_PREAMBLE_ENA
   rx_preamble_int,
`endif
   section,
   sect_e,
   ff_rx_clk,
   ff_rx_data,
   ff_rx_sop,
   ff_rx_eop,
   ff_rx_mod,
   ff_rx_err,
   ff_rx_err_stat,
`ifdef MTIP_PARITY_PROT
   ff_rx_data_par,
   ff_rx_ctrl_par,
   ff_rx_stat_par,
`endif
   ff_rx_rdy,
   ff_rx_dval,
   ff_rx_dsav,
   ff_rx_ts,
`ifdef USER_PREAMBLE_ENA
   ff_rx_preamble,
`endif
   ff_rx_empty
`ifdef FFMEM_EXTERNAL
   ,
   rxd_raddr,
   rxd_rden,
   rxd_rdata,
   rxd_recc,
   rxd_wren,
   rxd_waddr,
   rxd_wdata,
   rxs_raddr,
   rxs_rden,
   rxs_rdata,
   rxs_wren,
   rxs_waddr,
   rxs_wdata,
   rxp_raddr,
   rxp_rden,
   rxp_rdata,
   rxp_wren,
   rxp_waddr,
   rxp_wdata
`endif
  );



parameter FF_WIDTH      = 64;           //  Data width (not for change)
parameter DEPTH         = 256;          //  Data FIFO depth (max 512)
parameter ADDR_WIDTH    = 8;            //  Data FIFO address bits
parameter ST_DEPTH      = 256;          //  Status FIFO depth
parameter ST_ADDR_WIDTH = 8;            //  Status FIFO address bits
parameter TS_WIDTH      = 4;            //  Timestamp value width
parameter FF_OUT_REGISTERED = 0;        //  1: add output registers (extra delay) 0: direct memory output
parameter RX_STAT_DAT_WIDTH_64 = 25;    //  Status Data width


`ifdef USER_PREAMBLE_ENA
localparam SOP_WIDTH = TS_WIDTH+56;
`else
localparam SOP_WIDTH = TS_WIDTH;
`endif

`ifdef MTIP_PARITY_PROT
localparam SFF_WIDTH = 3+1+14+3+RX_STAT_DAT_WIDTH_64;   // stat par(3) + ctrl par + frm_length + mod(3) + status(RX_STAT_DAT_WIDTH_64);
`else
localparam SFF_WIDTH = 14+3+RX_STAT_DAT_WIDTH_64;   // frm_length + mod(3) + status(RX_STAT_DAT_WIDTH_64);
`endif


input   reset_rxclk;                    //  Asynchronous Reset - rxclk domain
input   reset_ff_rx_clk;                //  Asynchronous Reset - ff_rx_clk domain
input   rx_err_frm_disc;                //  Discard received errored frames
input   rx_short_disc;                  //  Discard received short frames
`ifdef MTIP_PARITY_PROT
input   par_odd;                        //  If 1, odd parity used, otherwise even parity
`endif
input   rx_sw_reset;                    //  FIFO sync reset (ff_rx_clk)
output  rx_sw_reset_done;               //  FIFO reset completed, pulse (ff_rx_clk)
input   rxclk;                          //  Rx Line Clock
input   [3 + RX_STAT_DAT_WIDTH_64-1:0] rx_stat_data;            //  Status Data with Last Word Modulo
input   rx_stat_wren;                   //  Status FIFO Write Enable
input   [63:0] rx_data_int;             //  Data from FIFO
output  rx_a_full;                      //  Data FIFO Almost Full
output  rx_septy;                       //  Data Section Available
input   rx_sop_int;                     //  Start of Packet
input   rx_eop_int;                     //  End of Packet
input   rx_wren_int;                    //  Data Receive FIFO Write Enable
input   [TS_WIDTH-1:0] rx_ts_int;       //  Receive Timestamp (synchronous to Rx clock domain)
`ifdef USER_PREAMBLE_ENA
input   [55:0] rx_preamble_int;         //  Receive Preamble
`endif
input   [ADDR_WIDTH-1:0] section;       //  Section Threshold Used to Genererate SAV
input   [ADDR_WIDTH-1:0] sect_e;        //  Section Threshold Used to Genererate SEPTY
input   ff_rx_clk;                      //  Receive Local Clock
output  [63:0] ff_rx_data;              //  Data Out
output  ff_rx_sop;                      //  Start of Packet
output  ff_rx_eop;                      //  End of Packet
output  [2:0] ff_rx_mod;                //  Last Word Modulo
output  ff_rx_err;                      //  Errored Packet Indication
output  [RX_STAT_DAT_WIDTH_64-1:0] ff_rx_err_stat;          //  Errored Packet Status Word
`ifdef MTIP_PARITY_PROT
output  [7:0] ff_rx_data_par;           //  Data Parity Out
output  ff_rx_ctrl_par;                 //  Control Parity Out
output  ff_rx_stat_par;                 //  Status Parity
`endif
input   ff_rx_rdy;                      //  PHY Application Ready
output  ff_rx_dval;                     //  Data Valid Strobe
output  ff_rx_dsav;                     //  Data Available
output  [TS_WIDTH-1:0] ff_rx_ts;        //  Timestamp of the frame
`ifdef USER_PREAMBLE_ENA
output  [55:0] ff_rx_preamble;          //  Receive Preamble
`endif
output  ff_rx_empty;                    //  FIFO has no data

`ifdef FFMEM_EXTERNAL   // External Memories

  output [ADDR_WIDTH-1:0]       rxd_raddr;    //  RX read address (ff_rx_clk)
  output                        rxd_rden;     //  RX read enable (ff_rx_clk)
  input  [FF_WIDTH-1:0]         rxd_rdata;    //  RX read data (ff_rx_clk)
  input                         rxd_recc;     //  RX read data ecc error (ff_rx_clk)
  output                        rxd_wren;     //  RX write (rx_clk)
  output [ADDR_WIDTH-1:0]       rxd_waddr;    //  RX write addres (rx_clk)
  output [FF_WIDTH-1:0]         rxd_wdata;    //  RX write data (rx_clk)

  wire   [ADDR_WIDTH-1:0]       rxd_raddr;
  wire                          rxd_rden;
  wire                          rxd_wren;
  wire   [ADDR_WIDTH-1:0]       rxd_waddr;
  wire   [FF_WIDTH-1:0]         rxd_wdata;

  output [ST_ADDR_WIDTH-1:0]    rxs_raddr;    //  RX read address (ff_rx_clk)
  output                        rxs_rden;     //  RX read enable (ff_rx_clk)
  input  [SFF_WIDTH-1:0]        rxs_rdata;    //  RX read data (ff_rx_clk)
  output                        rxs_wren;     //  RX write (rx_clk)
  output [ST_ADDR_WIDTH-1:0]    rxs_waddr;    //  RX write addres (rx_clk)
  output [SFF_WIDTH-1:0]        rxs_wdata;    //  RX write data (rx_clk)

  wire   [ST_ADDR_WIDTH-1:0]    rxs_raddr;    //  RX read address (ff_rx_clk)
  wire                          rxs_rden;     //  RX read enable (ff_rx_clk)
  wire                          rxs_wren;     //  RX write (rx_clk)
  wire   [ST_ADDR_WIDTH-1:0]    rxs_waddr;    //  RX write addres (rx_clk)
  wire   [SFF_WIDTH-1:0]        rxs_wdata;    //  RX write data (rx_clk)

  output [ST_ADDR_WIDTH-1:0]    rxp_raddr;    //  RX read address (ff_rx_clk)
  output                        rxp_rden;     //  RX read enable (ff_rx_clk)
  input  [SOP_WIDTH-1:0]        rxp_rdata;    //  RX read data (ff_rx_clk)
  output                        rxp_wren;     //  RX write (rx_clk)
  output [ST_ADDR_WIDTH-1:0]    rxp_waddr;    //  RX write addres (rx_clk)
  output [SOP_WIDTH-1:0]        rxp_wdata;    //  RX write data (rx_clk)

  wire   [ST_ADDR_WIDTH-1:0]    rxp_raddr;    //  RX read address (ff_rx_clk)
  wire                          rxp_rden;     //  RX read enable (ff_rx_clk)
  wire                          rxp_wren;     //  RX write (rx_clk)
  wire   [ST_ADDR_WIDTH-1:0]    rxp_waddr;    //  RX write addres (rx_clk)
  wire   [SOP_WIDTH-1:0]        rxp_wdata;    //  RX write data (rx_clk)

`endif

wire    rx_sw_reset_done;
wire    rx_a_full;
wire    rx_septy;
wire    [63:0] ff_rx_data;
wire    ff_rx_sop;
wire    ff_rx_eop;
wire    [2:0] ff_rx_mod;
wire    ff_rx_err;
wire    [RX_STAT_DAT_WIDTH_64-1:0] ff_rx_err_stat;
`ifdef MTIP_PARITY_PROT
wire    [7:0] ff_rx_data_par;
wire    ff_rx_ctrl_par;
wire    ff_rx_stat_par;
`endif
wire    ff_rx_dval;
wire    ff_rx_dsav;
wire    [TS_WIDTH-1:0] ff_rx_ts;
`ifdef USER_PREAMBLE_ENA
wire    [55:0] ff_rx_preamble;
`endif
wire    ff_rx_empty;

wire    rx_wren_int_g;                  //  Data write enable gated
wire    rx_stat_wren_g;                 //  Status write enable gated
wire    [FF_WIDTH-1:0] frx_data;        //  FIFO Write Data
wire    frx_sop;                        //  Start of Packet
wire    frx_eop;                        //  End of Packet
wire    frx_wren;                       //  Data Valid Strobe
wire    [TS_WIDTH-1:0] frx_ts;          //  Receive Timestamp
`ifdef USER_PREAMBLE_ENA
wire    [55:0] frx_preamble;            //  Receive Preamble
`endif
reg     frx_stat_wren;                  //  Receive Status FIFO Write Enable
reg     [RX_STAT_DAT_WIDTH_64-1:0] frx_stat_data;           //  Receive Status Data
reg     [2:0] frx_mod;                  //  Receive Data Modulo

//  Data FIFO Signals
//  -----------------
wire    [ADDR_WIDTH - 1:0] af_level;    //  Almost Full Threshold
wire    [ADDR_WIDTH - 1:0] ae_level;    //  Almost Empty Threshold
wire    [FF_WIDTH-1:0] ff_data_out;
wire    ff_data_out_err;                //  ecc error bit valid with ff_data_out
reg     ff_data_out_err_l;              //  ecc error bit valid with ff_data_out, latched
wire    ff_rden_int;
wire    ff_sav_int;
wire    ff_a_empty_int;
wire    ff_empty_int;

//  Status FIFO Signals
//  -------------------
wire    stat_wren;
wire    [SFF_WIDTH - 1:0] stat_in;
wire    stat_rden;
wire    [SFF_WIDTH - 1:0] stat_int;
wire    rx_stat_empty_int;
reg     rx_stat_empty;
wire    rx_stat_afull;
wire    rx_ff_afull;
wire    [13:0] frm_length;
reg     ffstat_empty;
wire    ffstat_empty_m;
reg     [13:0] ffstat_len;
reg     [3+ RX_STAT_DAT_WIDTH_64-1:0] ffstat_data;   // all without the ffstat_len
reg     ffstat_err;
wire    [2:0] ffstat_mod;

//  SOP FIFO Signals (Timestamp and Preamble)
//  -----------------------------------------
wire    sop_wren;
wire    sop_rden;
wire    sop_empty;
wire    [SOP_WIDTH-1:0] sop_din;
wire    [SOP_WIDTH-1:0] sop_dout;
reg     [SOP_WIDTH-1:0] sop_dout_r;

`ifdef MTIPSDPM_GEN_READEN
wire    sop_empty_n;
`endif

//  Controls
reg     w_ok;
wire    sw_reset_wclk;  //  sync reset on write clock domain
wire    sw_reset_rclk;  //  sync reset on read clock domain
wire    vcc;
wire    err_frm_disc_s; //  SYNC'ed to ff_rx_clk
wire    short_disc_s;   //  SYNC'ed to ff_rx_clk
wire    fdisc;          //  discard indication to FIFO read control
wire    fr_sop;
wire    fr_eop;
wire    fr_err;
wire    [2:0] fr_mod;
wire    fr_dval;

`ifdef MTIPSDPM_GEN_READEN
wire rx_stat_empty_n;   // inverted of rx_stat_empty
`endif

`ifdef MTIP_PARITY_PROT
// Parity calculation
wire    par_odd_rx;
wire    par_odd_ff;
wire    [7:0] rx_data_par_int_nxt;
wire    [7:0] rx_data_par_int;
wire    rx_ctrl_par_int;
wire    [2:0] rx_stat_par_int;
wire    ctrl_par_int_err;
wire    stat_par_int_err;
reg     ffstat_ctrl_par_err;
reg     ffstat_stat_par_err;
wire    ff_rx_ctrl_par_nxt;
wire    ff_rx_stat_par_nxt;
`endif


assign vcc = 1'b 1;

//  Synchronize configuration signals
//  ----------------------------------
mtip_xsync #(2) U_DISCS (
          .data_in({rx_short_disc, rx_err_frm_disc}),
          .reset(reset_ff_rx_clk),
          .clk(ff_rx_clk),
          .data_s({short_disc_s, err_frm_disc_s}));

`ifdef MTIP_PARITY_PROT

mtip_xsync #(1) U_PARRX (
          .data_in(par_odd),
          .reset(reset_rxclk),
          .clk(rxclk),
          .data_s(par_odd_rx));

mtip_xsync #(1) U_PARFF (
          .data_in(par_odd),
          .reset(reset_ff_rx_clk),
          .clk(ff_rx_clk),
          .data_s(par_odd_ff));

`endif

//  Sync reset controller in application side clock domain
//  ------------------------------------------------------
ff_sreset U_RST (
          //  local clock where reset occurs
          .reset_lclk(reset_ff_rx_clk),         //  application side clock
          .lclk(ff_rx_clk),
          .lsreset_in(rx_sw_reset),             //  trigger reset
          .lsreset_done(rx_sw_reset_done),      //  cycle complete pulse
          .lsreset_out(sw_reset_rclk),          //  read clock domain sync reset
          //  other clock domain
          .reset_oclk(reset_rxclk),             //  write side
          .oclk(rxclk),
          .osreset_out(sw_reset_wclk));

//  ------------------------------------------
//  Write blocking (e.g. after reset, protect)
//  ------------------------------------------

always @(posedge reset_rxclk or posedge rxclk)
   begin : wbk
   if (reset_rxclk == 1'b 1)
      begin
      w_ok <= 1'b 0;    //  write clock domain
      end
   else
      begin
      if (sw_reset_wclk == 1'b 1)
         begin
         w_ok <= 1'b 0;
         end
      else if (rx_sop_int == 1'b 1 & rx_wren_int == 1'b 1)
         begin
         //  assert only when not within writing a frame (e.g. reset in middle of reception)
         w_ok <= 1'b 1;
         end
      end
   end

//  -----------------
//  Internal wiring
//  -----------------

assign rx_wren_int_g  = (rx_sop_int | w_ok) & rx_wren_int;    // write only when valid cycle
assign rx_stat_wren_g = w_ok & rx_stat_wren;   // write only when valid cycle

assign frx_sop  = rx_sop_int;
assign frx_eop  = rx_eop_int;
assign frx_wren = rx_wren_int_g;
assign frx_ts   = rx_ts_int;
`ifdef USER_PREAMBLE_ENA
assign frx_preamble = rx_preamble_int;
`endif

//  -----------------
//  Data FIFO
//  -----------------

assign af_level = {{(ADDR_WIDTH-3){1'b 0}}, 3'b 110};
assign ae_level = {{(ADDR_WIDTH-3){1'b 0}}, 3'b 110};

`ifdef MTIP_PARITY_PROT

// Data parity calculation

assign rx_data_par_int_nxt = {(^rx_data_int[63:56]),
                              (^rx_data_int[55:48]),
                              (^rx_data_int[47:40]),
                              (^rx_data_int[39:32]),
                              (^rx_data_int[31:24]),
                              (^rx_data_int[23:16]),
                              (^rx_data_int[15:8]),
                              (^rx_data_int[7:0])};

assign rx_data_par_int = (par_odd_rx == 1'b 1) ? ~rx_data_par_int_nxt : rx_data_par_int_nxt;

assign frx_data = {rx_data_par_int, rx_data_int};

`else

assign frx_data = rx_data_int;

`endif

r_fifo_opt_12456e #(FF_WIDTH,DEPTH,ADDR_WIDTH) U_DFF (
          .reset_wclk(reset_rxclk),
          .reset_rclk(reset_ff_rx_clk),
          .sw_reset_wclk(sw_reset_wclk),
          .sw_reset_rclk(sw_reset_rclk),
          .wclk(rxclk),
          .wren(frx_wren),
          .din(frx_data),
          .rclk(ff_rx_clk),
          .rden(ff_rden_int),
          .dout(ff_data_out),
          .dout_err(ff_data_out_err),
          .section(section),
          .sect_e(sect_e),
          .af_level(af_level),
          .ae_level(ae_level),
          .sav(ff_sav_int),
          .septy(rx_septy),
          .afull(rx_ff_afull),
          .aempty(ff_a_empty_int),
          .empty(ff_empty_int)

         `ifdef FFMEM_EXTERNAL
          ,
          .m_raddr(rxd_raddr),
          .m_rden( rxd_rden ),
          .m_rdata(rxd_rdata),
          .m_recc( rxd_recc ),
          .m_wren( rxd_wren ),
          .m_waddr(rxd_waddr),
          .m_wdata(rxd_wdata)
         `endif
          );

//  ------------------------------------------------------------
//  SOP FIFO (timestamp and preamble) stored with start of frame
//  ------------------------------------------------------------

`ifdef USER_PREAMBLE_ENA
assign sop_din = {frx_preamble, frx_ts};
`else
assign sop_din = frx_ts;
`endif

assign sop_wren = frx_sop & frx_wren;   // wclk domain

`ifdef FFMEM_EXTERNAL
r_fifo_23e
`else
r_fifo_23
`endif
        #(.FF_WIDTH      (SOP_WIDTH),
            .ADDR_WIDTH  (ST_ADDR_WIDTH),
            .DEPTH       (ST_DEPTH),
            .AF_THRESHOLD(1) )          // unused anyway

        U_SOPFF (

          .reset_wclk(reset_rxclk),
          .reset_rclk(reset_ff_rx_clk),
          .sw_reset_wclk(sw_reset_wclk),
          .sw_reset_rclk(sw_reset_rclk),
          .wclk(rxclk),
          .wren(sop_wren),
          .din(sop_din),
          .rclk(ff_rx_clk),
          .rden(sop_rden),
        `ifdef MTIPSDPM_GEN_READEN
          .mem_rden(sop_empty_n),       // immediately produce output as long as non-empty
        `endif
          .dout(sop_dout),
          .afull(),
          .empty(sop_empty)
        `ifdef FFMEM_EXTERNAL
          ,
          .dout_err(),
          .m_raddr(rxp_raddr),
          .m_rden( rxp_rden ),
          .m_rdata(rxp_rdata),
          .m_recc( 1'b 0 ),
          .m_wren( rxp_wren ),
          .m_waddr(rxp_waddr),
          .m_wdata(rxp_wdata) 
        `endif
          );

`ifdef MTIPSDPM_GEN_READEN
assign    sop_empty_n = ~sop_empty;
`endif

// memory output register with zeroing when no data

always @(posedge reset_ff_rx_clk or posedge ff_rx_clk)
   begin : soporeg
   if (reset_ff_rx_clk == 1'b 1)
      begin
      sop_dout_r <= {SOP_WIDTH{1'b 0}};
      end
   else
      begin
      if (sop_empty == 1'b 1)
         begin
         sop_dout_r <= {SOP_WIDTH{1'b 0}};
         end
      else
         begin
         sop_dout_r <= sop_dout;
         end
      end
   end

assign ff_rx_ts = sop_dout_r[TS_WIDTH-1:0];

`ifdef USER_PREAMBLE_ENA

assign ff_rx_preamble = sop_dout_r[TS_WIDTH+55:TS_WIDTH];

`endif

//  -----------
//  Status FIFO
//  -----------

rx_ff_length U_LGHT (
          .clk(rxclk),
          .clk_ena(vcc),
          .reset(reset_rxclk),
          .rx_sop_int(frx_sop),
          .rx_eop_int(frx_eop),
          .rx_wren_int(frx_wren),
          .frm_length(frm_length));

//  Need to delay status write by one clock cycle.
//  Note: we know that the rx_stat_data values are valid even
//  after the status has been written for one additional cycle.
//  -----------------------------------------------------------

always @(posedge reset_rxclk or posedge rxclk)
   begin : process_1
   if (reset_rxclk == 1'b 1)
      begin
      frx_stat_wren <= 1'b 0;
      frx_stat_data <= {RX_STAT_DAT_WIDTH_64{1'b 0}};
      frx_mod <= {3{1'b 0}};
      end
   else
      begin
      frx_stat_wren <= rx_stat_wren_g;
      frx_stat_data <= rx_stat_data[RX_STAT_DAT_WIDTH_64-1:0];   // without modulo bits
      frx_mod <= rx_stat_data[RX_STAT_DAT_WIDTH_64-1 +3:RX_STAT_DAT_WIDTH_64];
      end
   end

`ifdef MTIP_PARITY_PROT

// Control parity calculation (will be re-calculated after the FIFO)

assign rx_ctrl_par_int = ^{frm_length, frx_mod};

// Status parity calculation (will be re-calculated after the FIFO)

assign rx_stat_par_int = {(^frx_stat_data[23:16]),
                          (^frx_stat_data[15:8]),
                          (^frx_stat_data[7:0])};

assign stat_in = {rx_ctrl_par_int, rx_stat_par_int, frm_length, frx_mod, frx_stat_data};

`else

assign stat_in = {frm_length, frx_mod, frx_stat_data};

`endif

assign stat_wren = frx_stat_wren;

`ifdef MTIPSDPM_GEN_READEN

        // when using read enable, the memory provides data out one cycle later.

        assign rx_stat_empty_n = ~rx_stat_empty_int;

        always@(posedge reset_ff_rx_clk or posedge ff_rx_clk)
        begin
                if( reset_ff_rx_clk==1'b 1 )
                begin
                        rx_stat_empty <= 1'b 1;
                end
                else
                begin
                        if( sw_reset_rclk==1'b 1 )
                        begin
                                rx_stat_empty <= 1'b 1;
                        end
                        else
                        begin
                                rx_stat_empty <= rx_stat_empty_int;
                        end
                end
        end

`else

        always@(rx_stat_empty_int)
        begin
                rx_stat_empty = rx_stat_empty_int;   // no delay needed normally
        end

`endif

`ifdef FFMEM_EXTERNAL
r_fifo_23e
`else
r_fifo_23
`endif
          #(.FF_WIDTH    (SFF_WIDTH),
            .ADDR_WIDTH  (ST_ADDR_WIDTH),
            .DEPTH       (ST_DEPTH),
            .AF_THRESHOLD(2) )

        U_SFF (

          .reset_wclk(reset_rxclk),
          .reset_rclk(reset_ff_rx_clk),
          .sw_reset_wclk(sw_reset_wclk),
          .sw_reset_rclk(sw_reset_rclk),
          .wclk(rxclk),
          .wren(stat_wren),
          .din(stat_in),
          .rclk(ff_rx_clk),
          .rden(stat_rden),
        `ifdef MTIPSDPM_GEN_READEN
          .mem_rden(rx_stat_empty_n),   // immediately produce output as long as non-empty
        `endif
          .dout(stat_int),
          .afull(rx_stat_afull),
          .empty(rx_stat_empty_int)
        `ifdef FFMEM_EXTERNAL
          ,
          .dout_err(),
          .m_raddr(rxs_raddr),
          .m_rden( rxs_rden ),
          .m_rdata(rxs_rdata),
          .m_recc( 1'b 0 ),
          .m_wren( rxs_wren ),
          .m_waddr(rxs_waddr),
          .m_wdata(rxs_wdata) 
        `endif
          );

assign rx_a_full = rx_ff_afull | rx_stat_afull;   // indicate FIFO full to MAC

//  --------------
//  Read Control
//  --------------

`ifdef MTIP_PARITY_PROT

assign ctrl_par_int_err = (stat_int[SFF_WIDTH-1] != ^(stat_int[SFF_WIDTH-4-1:RX_STAT_DAT_WIDTH_64])) ? 1'b 1 : 1'b 0;

assign stat_par_int_err = (stat_int[SFF_WIDTH-2:SFF_WIDTH-4] != {(^stat_int[23:16]),
                                                                 (^stat_int[15:8]),
                                                                 (^stat_int[7:0])}) ? 1'b 1 : 1'b 0;

`endif

//  Status FIFO memory output registers
//  -----------------------------------
always @(posedge reset_ff_rx_clk or posedge ff_rx_clk)
   begin : mreg
   if (reset_ff_rx_clk == 1'b 1)
      begin
      ffstat_empty <= 1'b 1;
      ffstat_data <= {(3+RX_STAT_DAT_WIDTH_64){1'b 0}};
      ffstat_len <= {14{1'b 0}};
    `ifdef MTIP_PARITY_PROT
      ffstat_ctrl_par_err <= 1'b 0;
      ffstat_stat_par_err <= 1'b 0;
    `endif
      ffstat_err <= 1'b 0;
      end
   else
      begin
      ffstat_empty <= rx_stat_empty;   // align empty with data pipeline delay below

      if (rx_stat_empty == 1'b 0)
         begin
       `ifdef MTIP_PARITY_PROT
         ffstat_data <= stat_int[SFF_WIDTH-4 - 14 - 1:0];
         ffstat_len  <= stat_int[SFF_WIDTH-4 - 1:SFF_WIDTH-4 - 14];
         ffstat_ctrl_par_err <= ctrl_par_int_err;
         ffstat_stat_par_err <= stat_par_int_err;
       `else
         ffstat_data <= stat_int[SFF_WIDTH - 14 - 1:0];
         ffstat_len  <= stat_int[SFF_WIDTH - 1:SFF_WIDTH - 14];
       `endif
         ffstat_err  <= stat_int[0] | stat_int[1] | stat_int[2] | stat_int[3] | stat_int[4] | stat_int[6];
         end
      else
         begin
         ffstat_data <= {(3+RX_STAT_DAT_WIDTH_64){1'b 0}};
         ffstat_len <= {14{1'b 0}};
       `ifdef MTIP_PARITY_PROT
         ffstat_ctrl_par_err <= 1'b 0;
         ffstat_stat_par_err <= 1'b 0;
       `endif
         ffstat_err <= 1'b 0;
         end
      end
   end

assign ffstat_empty_m   = ffstat_empty | rx_stat_empty;         // assert immediately, deassert delayed
assign ffstat_mod       = ffstat_data[RX_STAT_DAT_WIDTH_64 + 3 - 1:RX_STAT_DAT_WIDTH_64];                   // modulo follows after RX_STAT_DAT_WIDTH_64-bit data
assign fdisc            = (ffstat_err & err_frm_disc_s) |       // discard frame on error, if configured to do so (S&F only!)
                          (ffstat_data[4] & short_disc_s);      // discard short frames (stat(4) is short or fault)

ff_rctlpre_1d #(

                .WLEN_WIDTH(14),        // number of bits in the length vector
                .FF_WIDTH_MOD(3),       // size of modulo information
                .MIN_DISTANCE_ENA(1) )  // 1: make eop->next frame sop distance minimal (if 0 it is 3 cycles in between frames)

        U_CNTL (

          .reset_rclk(reset_ff_rx_clk),
          .rclk(ff_rx_clk),
          .sw_reset_rclk(sw_reset_rclk),
          .dff_aempty(ff_a_empty_int),
          .dff_sav(ff_sav_int),
          .dff_rden(ff_rden_int),
          .sff_empty(ffstat_empty_m),
          .sff_rden(stat_rden),
          .sop_rden(sop_rden),
          .frm_length(ffstat_len),      //  frame length (words)
          .fdisc(fdisc),
          .ferr(ffstat_err),
          .fmod(ffstat_mod),
          .fr_rden(ff_rx_rdy),
          .fr_sop(fr_sop),              //  ff_rx_sop,
          .fr_eop(fr_eop),              //  ff_rx_eop,
          .fr_err(fr_err),
          .fr_mod(fr_mod),
          .fr_dval(fr_dval),            //  ff_rx_dval,
          .fr_sav(ff_rx_dsav)
          );

assign ff_rx_err_stat   = ffstat_data[RX_STAT_DAT_WIDTH_64-1:0];

`ifdef MTIP_PARITY_PROT

assign ff_rx_ctrl_par_nxt = (par_odd_ff == 1'b 0) ? (^{ff_rx_sop, ff_rx_eop, ff_rx_mod}) : ~(^{ff_rx_sop, ff_rx_eop, ff_rx_mod});
assign ff_rx_stat_par_nxt = (par_odd_ff == 1'b 0) ? (^{ff_rx_err, ff_rx_err_stat}) : ~(^{ff_rx_err, ff_rx_err_stat});

assign ff_rx_ctrl_par = (ffstat_ctrl_par_err == 1'b 0) ? ff_rx_ctrl_par_nxt : ~ff_rx_ctrl_par_nxt;
assign ff_rx_stat_par = (ffstat_stat_par_err == 1'b 0) ? ff_rx_stat_par_nxt : ~ff_rx_stat_par_nxt;

`endif

//  Latch memory output error
//  -------------------------
always @(posedge reset_ff_rx_clk or posedge ff_rx_clk)
begin : eccerr
        if (reset_ff_rx_clk == 1'b 1)
        begin
                ff_data_out_err_l <= 1'b 0;
        end
        else
        begin
                if (sw_reset_rclk == 1'b 1 )
                begin
                        ff_data_out_err_l <= 1'b 0;
                end
                `ifdef FFMEM_EXTERNAL_DELAY2
                // 2-cycle memory delay
                else if (rx_dval_r == 1'b 1)
                begin
                        if( ff_data_out_err==1'b 1 & rx_eop_r==1'b 0 )
                        begin
                                ff_data_out_err_l <= 1'b 1;
                        end
                        else if( rx_eop_r==1'b 1 )
                        begin
                                // clear at frame boundaries
                                ff_data_out_err_l <= 1'b 0;
                        end
                end
                `else
                // 1-cycle memory delay (normal)
                else if (fr_dval == 1'b 1 )
                begin
                        if( ff_data_out_err==1'b 1 & fr_eop==1'b 0)
                        begin
                                ff_data_out_err_l <= 1'b 1;
                        end
                        else if( fr_sop==1'b 1 | fr_eop==1'b 1 )
                        begin
                                // clear at frame boundaries
                                ff_data_out_err_l <= 1'b 0;
                        end
                end
                `endif
        end
end

generate if( FF_OUT_REGISTERED==1 )
begin:FFOUTREG

`ifndef FFMEM_EXTERNAL_DELAY2
reg     [FF_WIDTH-1:0]  ff_data_out_r;
`endif
reg             rx_sop_r;
reg             rx_eop_r;
reg             rx_err_r;
reg     [2:0]   rx_mod_r;
reg             rx_dval_r;

// -----------------------------------------
// Additional output register to application
// -----------------------------------------
always @(posedge reset_ff_rx_clk or posedge ff_rx_clk)
   begin : tsreg2
   if (reset_ff_rx_clk == 1'b 1)
      begin
        `ifndef FFMEM_EXTERNAL_DELAY2
        ff_data_out_r   <= {FF_WIDTH{1'b 0}};
        `endif
        rx_sop_r        <= 1'b 0;
        rx_eop_r        <= 1'b 0;
        rx_err_r        <= 1'b 0;
        rx_mod_r        <= 3'b 000;
        rx_dval_r       <= 1'b 0;
      end
   else
      begin
        `ifndef FFMEM_EXTERNAL_DELAY2
        // produce only valid output data, suppressing any 'X'
        if( fr_dval==1'b 1 )
        begin
                ff_data_out_r <= ff_data_out;
        end
        `endif

        // align with extra delay
        rx_mod_r <= fr_mod;
        rx_eop_r <= fr_eop;
        `ifdef FFMEM_EXTERNAL_DELAY2
        rx_err_r <= fr_err;
        `else
        rx_err_r <= fr_err | ff_data_out_err_l | (ff_data_out_err & fr_dval);   // Memory error within very last word
        `endif
        rx_sop_r <= fr_sop & fr_dval;
        rx_dval_r <= fr_dval;
      end
   end

assign ff_rx_sop  = rx_sop_r;
assign ff_rx_eop  = rx_eop_r;
assign ff_rx_dval = rx_dval_r;
assign ff_rx_mod  = rx_mod_r;

`ifdef FFMEM_EXTERNAL_DELAY2
  assign ff_rx_err  = rx_err_r | ff_data_out_err_l | (ff_data_out_err & rx_dval_r);   // Memory error within very last word
  assign ff_rx_data = ff_data_out[63:0];
  `ifdef MTIP_PARITY_PROT
  assign ff_rx_data_par = ff_data_out[71:64];
  `endif
`else
  assign ff_rx_err  = rx_err_r;
  assign ff_rx_data = ff_data_out_r[63:0];
  `ifdef MTIP_PARITY_PROT
  assign ff_rx_data_par = ff_data_out_r[71:64];
  `endif
`endif

assign ff_rx_empty = ff_empty_int & ffstat_empty & rx_stat_empty;   // total empty indication

end
endgenerate

generate if( FF_OUT_REGISTERED!=1 )
begin:NOOUTREG

// ----------------------------------------------
// Direct low latency output, with combinatorials
// ----------------------------------------------
assign ff_rx_sop  = fr_sop & fr_dval;
assign ff_rx_eop  = fr_eop;
assign ff_rx_dval = fr_dval;

assign ff_rx_err  = fr_err | ff_data_out_err_l | (ff_data_out_err & fr_dval);
assign ff_rx_mod  = fr_mod;
assign ff_rx_data = ff_data_out[63:0];
`ifdef MTIP_PARITY_PROT
assign ff_rx_data_par = ff_data_out[71:64];
`endif

assign ff_rx_empty = ff_empty_int & rx_stat_empty;   // total empty indication (deassertion not delayed)

end
endgenerate


endmodule // module rx_ff64v2ts
